/*
 * Copyright 2022 The Open Islands Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openislands.oi.apiserver.manager;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.openislands.oi.constant.MessageCodeEnum;
import org.openislands.oi.constant.NodeState;
import org.openislands.oi.dao.entity.NodeDO;
import org.openislands.oi.error.LogicException;
import org.openislands.oi.manager.CommonNodeManager;
import org.openislands.oi.manager.CommonRouteManager;
import org.openislands.oi.manager.union.UnionPipelineManager;
import org.openislands.oi.pojo.dto.CommonResult;
import org.openislands.oi.pojo.dto.NodeDTO;
import org.openislands.oi.pojo.dto.NodePageDTO;
import org.openislands.oi.pojo.dto.RouteDTO;
import org.openislands.oi.scheduling.StateEngine;
import org.openislands.oi.util.ValueCopyUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

@Service
@SuppressWarnings("unused")
public class NodeManager {
    private static final Logger log = LoggerFactory.getLogger(NodeManager.class);

    @Resource
    private CommonNodeManager commonNodeManager;

    @Resource
    private CommonRouteManager commonRouteManager;

    @Resource
    private UnionPipelineManager unionPipelineManager;

    @Resource
    private StateEngine stateEngine;

    public Boolean addNode(NodeDTO nodeDTO) {
        Boolean isSelf = commonNodeManager.myNodeId(nodeDTO.getNodeId());
        LogicException.isTrue(!isSelf, MessageCodeEnum.DATA_UPDATE_ERROR, "can't add myself");
        nodeDTO.setState(NodeState.UNVERIFIED);
        nodeDTO.setEnabled(true);
        unionPipelineManager.fire(CommonNodeManager.ADD_NODE_AND_ROUTE, nodeDTO);
        return true;
    }

    public Boolean deleteNode(NodeDTO nodeDTO) {
        String nodeId = nodeDTO.getNodeId();
        NodeDTO oneDTO = queryByNodeId(nodeId);
        LogicException.nonNull(oneDTO, MessageCodeEnum.DATA_UPDATE_ERROR, "not found node " + nodeId);
        if (commonNodeManager.isCanDeleteNodeState(oneDTO.getState())) {
            // unverified delete directly
            unionPipelineManager.fire(CommonNodeManager.DELETE_NODE_AND_ROUTE, nodeDTO);
            return true;
        } else if (stateEngine.isCooperationNodeState(oneDTO.getState())) {
            // wait break sync delete
            CommonResult<Object> result = commonNodeManager.updateNodeState(nodeDTO, NodeState.DELETED);
            LogicException.isTrue(result.isSuccess(), result.getCode(), result.getMessage());
            return result.isSuccess();
        }
        // skip deleted state
        throw new LogicException(MessageCodeEnum.DATA_UPDATE_ERROR, "node already deleted");
    }

    public Boolean updateNodeInfo(NodeDTO nodeDTO) {
        return commonNodeManager.updateNodeInfo(nodeDTO);
    }

    public Boolean resetNode(NodeDTO nodeDTO) {
        // update node state
        CommonResult<Object> result = commonNodeManager.updateNodeState(nodeDTO, nodeDTO.getState());
        LogicException.isTrue(result.isSuccess(), result.getCode(), result.getMessage());
        return true;
    }

    public NodeDTO queryByNodeId(String nodeId) {
        return commonNodeManager.queryByNodeId(nodeId);
    }

    public NodeDTO queryNodeDetails(String nodeId) {
        if ("0".equals(nodeId) || commonNodeManager.myNodeId(nodeId)) {
            return commonNodeManager.myNodeDTO();
        }

        // query info
        NodeDTO nodeDTO = queryByNodeId(nodeId);
        if (Objects.isNull(nodeDTO)) return null;
        // query route
        List<RouteDTO> routeDTOList = commonRouteManager.queryByNodeId(nodeId);
        nodeDTO.setRouteDTOList(routeDTOList);
        return nodeDTO;
    }

    @SuppressWarnings("unchecked")
    public Page<NodeDTO> queryPage(NodePageDTO pageDTO) {
        // paging query
        final SFunction<NodeDO, Object> orderFields = pageDTO.getOrderFields();
        final Page<NodeDO> nodePartyDOPage = commonNodeManager.getNodeManager().page(
                new Page<>(pageDTO.getPageNum(), pageDTO.getPageSize())
                , new QueryWrapper<NodeDO>().lambda()
                        .eq(Objects.nonNull(pageDTO.getNodeId()), NodeDO::getNodeId, pageDTO.getNodeId())
                        .like(Objects.nonNull(pageDTO.getNodeName()), NodeDO::getNodeName, pageDTO.getNodeName())
                        .eq(Objects.nonNull(pageDTO.getOwner()), NodeDO::getOwner, pageDTO.getOwner())
                        .eq(Objects.nonNull(pageDTO.getState()), NodeDO::getState, pageDTO.getState())
                        .orderByAsc(pageDTO.getOrderRule(), orderFields)
                        .orderByDesc(!pageDTO.getOrderRule(), orderFields)
        );

        final IPage<NodeDTO> nodeDTOIPage = nodePartyDOPage.convert(nodePartyDO ->
                ValueCopyUtils.copyProperties(nodePartyDO, new NodeDTO())
        );

        // query node route
        for (NodeDTO nodeDTO : nodeDTOIPage.getRecords()) {
            final List<RouteDTO> routeDOList = commonRouteManager.queryByNodeId(nodeDTO.getNodeId());
            nodeDTO.setRouteDTOList(routeDOList);
        }

        return ((Page<NodeDTO>) nodeDTOIPage);
    }
}
